﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace RobotMapper
{
    internal static class RobotMapperHelper
    {
        /// <summary>
        /// 将源的属性值赋值给目标的属性 只能处理系统泛型集合、系统类型、枚举类型
        /// True:表示已做处理
        /// </summary>
        public static bool SetTargetValue<T, M>(T source, M target, PropertyInfo sourceProperty, PropertyInfo destinationProperty)
        {
            //源中不存在的 Ignore
            if (sourceProperty == null) return true;

            //源不可读 则Ignore
            if (!sourceProperty.CanRead) return true;

            //目标不可写 则Ignore
            if (!destinationProperty.CanWrite) return true;

            Type sourcePropertyType = sourceProperty.PropertyType;
            Type destinationPropertyType = destinationProperty.PropertyType;

            object oldValue = sourceProperty.GetValue(source, null);
            if (oldValue == null) return true;

            //目标为枚举
            if (destinationPropertyType.IsEnum)
            {
                //如果该枚举值不在目标中存在则忽略
                if (!destinationPropertyType.IsEnumDefined(oldValue.ToString())) return true;
                //为目标枚举赋值
                destinationProperty.SetValue(target, Enum.Parse(destinationPropertyType, oldValue.ToString()), null);
                return true;
            }
            
            //非泛型系统对象 直接赋值
            if (!destinationPropertyType.IsGenericType && Filter(destinationPropertyType))
            {
                destinationProperty.SetValue(target, oldValue, null);
                return true;
            }

            //目标属性为可空类型 
            if (destinationPropertyType.IsGenericType && destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                //可空枚举 
                if (destinationGenericType.IsEnum)
                {
                    string value = Enum.GetName(oldValue.GetType(), oldValue);
                    if (!destinationGenericType.IsEnumDefined(value)) return true;
                    destinationProperty.SetValue(target, Enum.Parse(destinationGenericType, value), null);
                    return true;
                }
                destinationProperty.SetValue(target, oldValue, null);
                return true;
            }

            //目标为数组(系统内的 直接赋值)
            if (destinationPropertyType.IsArray&& Filter(destinationPropertyType.GetElementType()))
            {
                destinationProperty.SetValue(target, oldValue, null);
                return true;
            }

            //属性为系统泛型集合(非接口，支持枚举集合)
            if (destinationPropertyType.IsGenericType && !destinationPropertyType.IsInterface&&(typeof(IEnumerable).IsAssignableFrom(destinationPropertyType)))
            {
                var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                var sourceGenericType = sourcePropertyType.GetGenericArguments()[0];

                //系统对象直接赋值
                if (Filter(destinationGenericType))
                {
                    //为目标属性赋值
                    destinationProperty.SetValue(target, oldValue, null);
                    return true;
                }

                //非枚举需返回False
                if (!destinationGenericType.IsEnum) return false;

                var sValue = oldValue as IEnumerable;

                //创建目标值集合
                var dValue = Activator.CreateInstance(destinationPropertyType) as IList;
                foreach (var item in sValue)
                {
                    //目标对象是枚举
                    if (destinationGenericType.IsEnum)
                    {
                        //将源值转化为字符串
                        string value = Enum.GetName(sourceGenericType, item);
                        //如果该枚举值不在目标中存在则忽略
                        if (!destinationGenericType.IsEnumDefined(value)) continue;
                        //为目标枚举赋值
                        dValue.Add(Enum.Parse(destinationGenericType, value));
                    }
                }
                destinationProperty.SetValue(target, dValue, null);
                return true;
            }

            //属性为系统泛型集合(接口 支持 IList ICollection IEnumerable)
            if (destinationPropertyType.IsInterface &&
                (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IList<>)) ||
                destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>)) ||
                destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>))))
            {
                var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                var sourceGenericType = sourcePropertyType.GetGenericArguments()[0];

                //系统对象直接赋值
                if (Filter(destinationGenericType))
                {
                    //为目标属性赋值
                    destinationProperty.SetValue(target, oldValue, null);
                    return true;
                }

                //非枚举需返回False
                if (!destinationGenericType.IsEnum) return false;

                var sValue = oldValue as IEnumerable;

                //创建目标值集合
                var dValue = Activator.CreateInstance<ArrayList>();
                foreach (var item in sValue)
                {
                    //目标对象是枚举
                    if (destinationGenericType.IsEnum)
                    {
                        //将源值转化为字符串
                        string value = Enum.GetName(sourceGenericType, item);
                        //如果该枚举值不在目标中存在则忽略
                        if (!destinationGenericType.IsEnumDefined(value)) continue;
                        //为目标枚举赋值
                        dValue.Add(Enum.Parse(destinationGenericType, value));
                    }
                }
                object objNewDValue = null;
                var newDValue = InvokeExtensionMethod(dValue, destinationGenericType,"OfType");
                if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IList<>)))
                    objNewDValue = InvokeExtensionMethod(newDValue, destinationGenericType,"ToList");
                if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(ICollection<>)))
                    objNewDValue = InvokeExtensionMethod(newDValue, destinationGenericType,"ToList");
                if (destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>)))
                    objNewDValue = InvokeExtensionMethod(newDValue, destinationGenericType, "ToList");
                destinationProperty.SetValue(target, objNewDValue, null);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 调用扩展方法
        /// </summary>
        public static object InvokeExtensionMethod(object dValue, Type destinationGenericType, string methodName)
        {
            MethodInfo mi = typeof(Enumerable).GetMethod(methodName);
            MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { destinationGenericType });
            var newDValue = mi2.Invoke(null, new object[] { dValue }) as IEnumerable;
            return newDValue;
        }

        public static bool NameMatches(string memberName, string nameToMatch)
        {
            return string.Compare(memberName, nameToMatch, StringComparison.OrdinalIgnoreCase) == 0;
        }

        /// <summary>
        /// 根据上级引用找到本体类型形成循环引用的属性
        /// </summary>
        /// <param name="superior">上级引用</param>
        /// <param name="type">本体类型</param>
        /// <returns></returns>
        public static List<RefType> GetCircalRefType(List<Type> superiors, Type type)
        {
            var allRefType = GetAllRefType(type);
            return allRefType.Where(a => superiors.Contains(a.RefrenceType)).ToList();
        }

        /// <summary>
        /// 找出所有自引用属性
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static List<RefType> GetSelfRefType(Type type)
        {
            return GetAllRefType(type).Where(a => a.RefrenceType == type).ToList();
        }

        /// <summary>
        /// 找出某引用的所有引用+属性
        /// </summary>
        public static List<RefType> GetAllRefType(Type type)
        {
            PropertyInfo[] sourceProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var genericPros = sourceProperties
                .Where(a => a.PropertyType.IsGenericType && !Filter(a.PropertyType.GetGenericArguments()[0]))
                .Select(a => new RefType() { PropertyName = a.Name, RefrenceType = a.PropertyType.GetGenericArguments()[0] });
            var notgenercPros = sourceProperties
                .Where(a => !a.PropertyType.IsGenericType && !Filter(a.PropertyType))
                .Select(a => new RefType() { PropertyName = a.Name, RefrenceType = a.PropertyType });
            var allRefPros = genericPros.Union(notgenercPros);
            return allRefPros.ToList();
        }

        public static bool Filter(Type type)
        {
            return type.FullName.StartsWith("System.");
        }

        public static string GetPropertyName<T>(Expression<Func<T, object>> exp)
        {
            var rtn = "";
            if (exp.Body is UnaryExpression)
            {
                rtn = ((MemberExpression)((UnaryExpression)exp.Body).Operand).Member.Name;
            }
            else if (exp.Body is MemberExpression)
            {
                rtn = ((MemberExpression)exp.Body).Member.Name;
            }
            else if (exp.Body is ParameterExpression)
            {
                rtn = ((ParameterExpression)exp.Body).Type.Name;
            }
            if (!rtn.Contains(".")) return rtn;
            return rtn.Split('.')[1];
        }

        public static object CreatObject(Type objectType)
        {
            ConstructorInfo defaultCtor = objectType.GetConstructor(new Type[] { });
            DynamicMethod dynMethod = new DynamicMethod(
                name: string.Format("_{0:N}", Guid.NewGuid()),
                returnType: objectType,
                parameterTypes: null);
            var gen = dynMethod.GetILGenerator();
            gen.Emit(OpCodes.Newobj, defaultCtor);
            gen.Emit(OpCodes.Ret);
            var objCreator = dynMethod.CreateDelegate(typeof(Func<object>)) as Func<object>;
            return objCreator();
        }

        public static T CreatObject<T>()
            where T : class
        {
            return (CreatObject(typeof(T)) as T);
        }

        /// <summary>
        /// 所有上级
        /// </summary>
        /// <param name="maps">所有关系</param>
        /// <param name="map">上级</param>
        /// <param name="resultMaps"></param>
        public static void GetAllParentMaps(List<RelationMap> maps, RelationMap map, List<RelationMap> resultMaps)
        {
            if (!map.SupId.HasValue) return;
            var parentMap = maps.SingleOrDefault(a => a.Id == map.SupId);
            if (parentMap == null) return;
            resultMaps.Add(parentMap);
            GetAllParentMaps(maps, parentMap, resultMaps);
        }

        /// <summary>
        /// 获取所有上级Map
        /// </summary>
        /// <param name="maps">当前所有Map</param>
        /// <param name="map">当前Map</param>
        /// <param name="results">结果集</param>
        /// <returns></returns>
        public static List<RelationMap> GetMapParentMaps(List<RelationMap> maps, RelationMap map, List<RelationMap> results = null)
        {
            //表示第一次层
            if (results == null)
            {
                results = new List<RelationMap>();
                results.Add(map);
            }
            if (!map.SupId.HasValue) return results;
            var mapP = maps.SingleOrDefault(a => a.Id == map.SupId);
            if (mapP != null) results.Add(mapP);
            GetMapParentMaps(maps, mapP, results);
            return results;
        }

        public static void GetRelationMap(Type type, RelationMap map, List<RelationMap> maps)
        {
            //找出maps中所有的父级Maps
            var parentMaps = new List<RelationMap>();
            GetAllParentMaps(maps, map, parentMaps);
            //需加上上级
            parentMaps.Add(map);
            //获取所有属性
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var item in properties)
            {
                Type propertyType = item.PropertyType;
                if (propertyType.IsGenericType)
                {
                    var genericType = propertyType.GetGenericArguments()[0];
                    //系统对象
                    if (Filter(genericType)) continue;
                    if (parentMaps.Count(a => a.TheType == genericType) > 0) continue;
                    var newMap = new RelationMap()
                    {
                        Id = Guid.NewGuid(),
                        TheType = genericType,
                        PropertyName = item.Name,
                        SupId = map.Id,
                    };
                    maps.Add(newMap);
                    GetRelationMap(genericType, newMap, maps);
                    continue;
                }
                if (Filter(propertyType)) continue;
                if (parentMaps.Count(a => a.TheType == propertyType) > 0) continue;
                var newPMap = new RelationMap()
                {
                    Id = Guid.NewGuid(),
                    TheType = propertyType,
                    PropertyName = item.Name,
                    SupId = map.Id,
                };
                maps.Add(newPMap);
                GetRelationMap(propertyType, newPMap, maps);
            }
        }

        /// <summary>
        /// 获取对象属性值
        /// </summary>
        public static object GetPropertyValue<T>(T obj, Type type, PropertyInfo property)
        {
            return new PropertyDynamicGetter<T>().Execute(obj, property.Name);
        }

        public static M TransExp<T, M>(T tIn)
            where T : class
            where M : class
        {
            if (tIn == null) return null;

            //构建参数 传入T
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");

            //将M的所有属性通过T绑定
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(M).GetProperties())
            {
                MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }

            //无属性 返回
            if (memberBindingList.Count == 0) return Activator.CreateInstance<M>();

            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(M)), memberBindingList.ToArray());
            Expression<Func<T, M>> lambda = Expression.Lambda<Func<T, M>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
            Func<T, M> func = lambda.Compile();
            return func(tIn);
        }
    }
}
